/*
 * JSFConsole - Lightweight JSF Application Monitor
 * Copyright (C) 2009  Grzegorz Bugaj
 * http://www.gregbugaj.com
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.gregbugaj.jsfconsole.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
 * Reflection utility
 * @author devil
 *
 */
public final class ReflectUtil {
	/**
	 * Prevent instantiation of the class
	 */
	private ReflectUtil(){
		
	}
	
	private final static Map<Class<?>, Class<?>> simpleTypes = new Hashtable<Class<?>, Class<?>>();
	static {
		simpleTypes.put(String.class, String.class);
		simpleTypes.put(Boolean.class, Boolean.class);
		simpleTypes.put(boolean.class, boolean.class);
		simpleTypes.put(Byte.class, Byte.class);
		simpleTypes.put(byte.class, byte.class);
		simpleTypes.put(Short.class, Short.class);
		simpleTypes.put(short.class, short.class);
		simpleTypes.put(Integer.class, Integer.class);
		simpleTypes.put(int.class, int.class);
		simpleTypes.put(Long.class, Long.class);
		simpleTypes.put(long.class, long.class);
		simpleTypes.put(Float.class, Float.class);
		simpleTypes.put(float.class, float.class);
		simpleTypes.put(Double.class, Double.class);
		simpleTypes.put(double.class, double.class);
		simpleTypes.put(Character.class, Character.class);
		simpleTypes.put(char.class, char.class);
		simpleTypes.put(BigDecimal.class, BigDecimal.class);
		simpleTypes.put(StringBuffer.class, StringBuffer.class);
		simpleTypes.put(BigInteger.class, BigInteger.class);
		simpleTypes.put(Class.class, Class.class);
		simpleTypes.put(java.sql.Date.class, java.sql.Date.class);
		simpleTypes.put(java.util.Date.class, java.util.Date.class);
		simpleTypes.put(Time.class, Time.class);
		simpleTypes.put(Timestamp.class, Timestamp.class);
		simpleTypes.put(Calendar.class, Calendar.class);
		simpleTypes.put(GregorianCalendar.class, GregorianCalendar.class);
		simpleTypes.put(URL.class, URL.class);
		simpleTypes.put(Object.class, Object.class);
	}


	public static boolean isSimpleType(final Object object) {
		if (object == null) { return false; }
		return isSimpleType(object.getClass());
	}

	public static boolean isSimpleType(final Class<?> clazz) {
		if (clazz == null) { return false; }
		return simpleTypes.containsKey(clazz);
	}



	public static boolean isMapType(final Class<?> clazz) {
		boolean lvReturn = false;
		if (clazz != null) {
			lvReturn = (Map.class.isAssignableFrom(clazz));
		}
		return lvReturn;
	}

	public static boolean isMapType(final Object object) {
		boolean lvReturn = false;
		if (object == null) { 
			lvReturn = false;
		}
		else if (object instanceof Map) {
			lvReturn = true;
		}

		return lvReturn;
	}

	public static boolean isCollection(final Object object) {
		boolean lvReturn = false;
		if (object == null) { 
			lvReturn = false;
		}else{
			lvReturn=isCollection(object.getClass());
		}
		return lvReturn;
	}

	public static boolean isCollection(final Class<?> clazz) {
		boolean lvReturn = false;
		if (clazz == null) {
			lvReturn = false;
		}
		else if (Collection.class.isAssignableFrom(clazz)) {
			lvReturn = true;
		} 
		return lvReturn;
	}

	public static boolean isArray(final Object obj) {
		if (obj == null) {
			return false;
		}		
		return isArray(obj.getClass());
	}

	public static boolean isArray(final Class<?> clazz) {
		boolean lvReturn = false;
		if (clazz == null) {
			lvReturn = false;
		}else if(clazz.isArray()) {
			lvReturn = true;
		}
		return lvReturn;
	}


	public static boolean isEnum(final Object obj) {
		if (obj == null) {
			return false;
		}		
		return isEnum(obj.getClass());
	}

	public static boolean isEnum(final Class<?> clazz) {
		boolean lvReturn = false;
		if (clazz == null) {
			lvReturn = false;
		}else if (clazz.isEnum()) {
			lvReturn = true;
		}
		return lvReturn;
	}
	/**
	 * Get value of the method
	 * @param value
	 * @param method
	 * @return
	 * @throws Exception
	 */
	public static Object getMethodValue(final Object value, final Method method) throws Exception {
		Object propertyValue = null;				
		try {
			propertyValue = method.invoke(value, new Object[0]);
		} catch (Exception e) {
			throw new Exception("Cannot get field " + method.getDeclaringClass().getName() + "."
					+ method.getName());
		}		
		return propertyValue;
	}



	/**
	 * Try to make a field accessible 
	 * @param field to make accessible
	 * @return true if the field is accessible or if it have been made accessible
	 */
	public static boolean makeAccessible(Field field) {
		boolean retVal=true;
		if(!field.isAccessible()){
			try{
				field.setAccessible(true);				
			}catch(SecurityException sec){						
				retVal=false;				
			}
		}		
		return retVal;
	}

	public static String createRefId(Object obj) {		
		//Integer.toHexString(System.identityHashCode(value));
		return obj.getClass().getName()+"::"+obj.hashCode();
	}

	/*
	public static boolean isProxy(Object value) {
		return isProxy(value.getClass());
	}

	public static boolean isProxy(Class<?> clazz) {
		return Proxy.isProxyClass(clazz);
	}
	 */

}
